package com.bytz.modules.cms.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.script.ScriptUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bytz.common.exception.BytzBootException;
import com.bytz.modules.cms.order.constant.OrderConstants;
import com.bytz.modules.cms.order.entity.OrderDesignFlowRule;
import com.bytz.modules.cms.order.entity.OrderFlowRule;
import com.bytz.modules.cms.order.mapper.OrderDesignFlowRuleMapper;
import com.bytz.modules.cms.order.model.*;
import com.bytz.modules.cms.order.service.IOrderDesignFlowRuleService;
import com.bytz.modules.cms.order.service.IOrderFlowRuleService;
import com.bytz.modules.cms.order.service.IOrderFlowRuleTestCaseService;
import com.bytz.modules.cms.order.util.ScriptEvalUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.script.Bindings;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 订单流程规则服务
 * @author shanxuezhong
 */
@Service
public class OrderDesignFlowRuleServiceImpl extends ServiceImpl<OrderDesignFlowRuleMapper, OrderDesignFlowRule> implements IOrderDesignFlowRuleService {

    @Autowired
    private IOrderFlowRuleService orderFlowRuleService;

    @Autowired
    private IOrderFlowRuleTestCaseService orderFlowRuleTestCaseService;

    @Override
    public OrderDesignFlowRuleListVo getAll() {
        Map<String, OrderFlowRule> publishedMap = orderFlowRuleService.list().stream()
                .collect(Collectors.toMap(OrderFlowRule::getId, v -> v));

        List<OrderDesignFlowRuleOut> allData = list().stream()
                .map(p -> {
                    OrderDesignFlowRuleOut out = BeanUtil.toBean(p, OrderDesignFlowRuleOut.class);
                    // 判断是不是和发布的是一个版本
                    OrderFlowRule published = publishedMap.get(out.getId());
                    out.setSameAsPublished(published != null && Objects.equals(out.getVersion(), published.getVersion()));

                    return out;
                }).collect(Collectors.toList());

        OrderDesignFlowRuleListVo result = new OrderDesignFlowRuleListVo();

        result.setGlobalScript(allData.stream()
                .filter(p -> OrderConstants.OrderFlowRuleType.GLOBAL.equals(p.getType()))
                .findFirst()
                .orElse(null));

        result.setPrivateScripts(allData.stream()
                .filter(p -> OrderConstants.OrderFlowRuleType.PRIVATE.equals(p.getType()))
                .sorted(Comparator.comparing(OrderDesignFlowRule::getSort))
                .collect(Collectors.toList()));

        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateSaveObject(OrderDesignFlowRuleSaveRo saveRo) {
        List<OrderDesignFlowRule> allPrivateScripts = getAllPrivateScripts();
        Map<String, OrderDesignFlowRule> allPrivateScriptMap = allPrivateScripts.stream()
                .collect(Collectors.toMap(OrderDesignFlowRule::getId, v -> v));

        // 验证
        List<String> errors = new LinkedList<>();
        List<OrderDesignFlowRuleIn> needUpdates = new LinkedList<>();
        for (OrderDesignFlowRuleIn script : saveRo.getScripts()) {
            OrderDesignFlowRule scriptInDb = allPrivateScriptMap.get(script.getId());
            if (scriptInDb == null) {
                errors.add("数据库中找不到节点【" + script.getActivityName() + "】的脚本无法更新");
            } else {
                if (!Objects.equals(scriptInDb.getVersion(), script.getVersion())) {
                    errors.add(String.format("节点【%s】数据库中的版本为%d，传入的为%d，无法更新",
                            script.getActivityName(), scriptInDb.getVersion(), script.getVersion()));
                } else if (!Objects.equals(scriptInDb.getScript(), script.getScript())) {
                    // 找出需要更新的脚本
                    needUpdates.add(script);
                }
            }
        }

        if (errors.size() > 0) {
            throw new BytzBootException(String.join("; ", errors));
        }

        // 更新
        updateBatchById(needUpdates.stream()
                .map(p -> {
                    OrderDesignFlowRule orderDesignFlowRule = BeanUtil.toBean(p, OrderDesignFlowRule.class);
                    // 升版本号
                    orderDesignFlowRule.setVersion(orderDesignFlowRule.getVersion() + 1);

                    return orderDesignFlowRule;
                }).collect(Collectors.toList()));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void publish(OrderDesignFlowRuleSaveRo saveRo) {
        String globalScript = getGlobalScript();

        // 先验证下脚本都能编译
        for (OrderDesignFlowRuleIn script : saveRo.getScripts()) {
            try {
                ScriptUtil.compile(ScriptEvalUtil.combineScript(globalScript, script.getScript()));
            } catch (Exception ex) {
                throw new BytzBootException("节点【" + script.getActivityName() + "】的脚本有编译错误：" + ex.getMessage(), ex);
            }
        }

        // 再保存
        updateSaveObject(saveRo);

        // 发布
        orderFlowRuleService.publish(list());
    }

    @Override
    public OrderDesignFlowRuleTestOut testScript(OrderDesignFlowRuleTestIn ruleTestVo) throws Exception {
        // 组测试数据
        OrderFlowRuleTestCaseSaveRo testCaseSaveRo = orderFlowRuleTestCaseService.getSaveRoById(ruleTestVo.getTestCaseId());
        if (testCaseSaveRo == null) {
            throw new BytzBootException("找不到测试用例数据");
        }

        Bindings bindings = ScriptEvalUtil.createBinding(testCaseSaveRo.getOrder(), testCaseSaveRo.getOrderItems());

        // 组测试脚本执行测试
        String globalScript = getGlobalScript();
        List<OrderDesignFlowRuleTestResult> results = new LinkedList<>();
        for (OrderDesignFlowRuleIn script : ruleTestVo.getScripts()) {
            OrderDesignFlowRuleTestResult result = new OrderDesignFlowRuleTestResult();
            result.setScript(ScriptEvalUtil.combineScript(globalScript, script.getScript()));
            result.setErrored(true);
            result.setId(script.getId());
            result.setActivityName(script.getActivityName());
            try {
                Object obj = ScriptEvalUtil.testScript(result.getScript(), bindings);
                result.setResult(String.valueOf(obj));
                if (obj instanceof Boolean) {
                    result.setErrored(false);
                } else {
                    result.setMessage("结果不是布尔型");
                }
            } catch (Exception e) {
                result.setMessage(e.getMessage());
            }

            results.add(result);
        }

        return new OrderDesignFlowRuleTestOut(results);
    }

    /**
     * 获取全部私有脚本
     */
    private List<OrderDesignFlowRule> getAllPrivateScripts() {
        return lambdaQuery()
                .eq(OrderDesignFlowRule::getType, OrderConstants.OrderFlowRuleType.PRIVATE)
                .list();
    }

    /**
     * 获取全局脚本
     */
    private String getGlobalScript() {
        return getGlobalDesignFlowRule().map(OrderDesignFlowRule::getScript).orElse("");
    }

    /**
     * 查询全局脚本对象
     */
    private Optional<OrderDesignFlowRule> getGlobalDesignFlowRule() {
        return lambdaQuery()
                .eq(OrderDesignFlowRule::getType, OrderConstants.OrderFlowRuleType.GLOBAL)
                .oneOpt();
    }
}
